#pragma once
#include "Semantic.h"
#include <vector>
#include "DAEFloat4.h"
#include "DAEFloat3.h"
#include "DAEFloat2.h"
#include "DAEMatrix.h"

typedef unsigned char byte;

struct SemanticDescriptor
{
	SEMANTIC semantic;
	DATATYPE dataType;
	int nrOfElements;
	// same semantic, different index.
	int index;
	// indicates if the values should be interpolated, useful when inserting a vertex.
	bool interpolate;
};

using namespace std;
/**
* This class contains a list of vertices. The list can be configured
* to contain several different formats, such as VertexVNT, VertexVNTT
* and so on.
*
* The vertex list can grow to provide room for more vertices.
*
* The vertex list contains a mapping to insure that the data is copied
* to the correct location in the list. 
*
* The vertex list also provides support for serialization , to speed
* up the loading process.
*
* The vertex list can also be used to create a dynamic buffer for the
* small objects in a scene.
*
* @author Koen Samyn
*/ 
class VertexList
{
public:
	/**
	* Creates a new VertexList object with the provided
	* initialsize.
	* @param initialSize the initial number of vertices.
	*/
	VertexList(int initialSize);
	/**
	* Creates a new VertexList object 
	* @param initialSize the initial number of vertices.
	* @param semanticList the list of semantics as a vector.
	* @param sizeList the size for each semantic (for example a texcoord semantic could
	* contain 2 floats or 3)
	* @param adjustCurrentSize if this parameter is false, the provided initialSize provides
	* room for growth, setting this parameter to true indicates that the current size
	* will be adjusted.
	*/ 
	VertexList(int initialSize, vector<SEMANTIC> semanticList, vector<int> sizeList,bool adjustCurrentSize=false);

	/**
	* Creates a new VertexList object 
	* @param initialSize the initial number of vertices.
	* @param semanticList the list of semantics as a vector.
	* @param adjustCurrentSize if this parameter is false, the provided initialSize provides
	* room for growth, setting this parameter to true indicates that the current size
	* will be adjusted.
	*/ 
	VertexList(int initialSize, vector<SemanticDescriptor> semanticList,bool adjustCurrentSize=false);
	
	/**
	* Adds a semantic to the vertex list. This will clear and resize the current vertex data.
	* It is best to use this method only when the vertex list size is still zero.
	* @param semantic the semantic to add.
	* @param size the size (number of floats) for the semantic.
	*/
	void AddSemantic(SEMANTIC semantic, int size);

	/**
	* Adds a semantic to the vertex list. This will clear and resize the current vertex data.
	* It is best to use this method only when the vertex list size is still zero.
	* @param semantic the semantic to add.
	* @param defaultValue the default value.
	*/
	void AddSemantic(SemanticDescriptor sd, FXValue* defaultValue);

	/**
	* Destroys the vertex list.
	*/
	virtual ~VertexList(void);
	/**
	* Returns the size of the VertexList
	* @return the current size.
	*/
	int GetSize();

	/**
	* Specialized function to get a general FXValue object from the vertexlist.
	* @param vertexindex the index of the vertex.
	* @param semantic the semantic to get.
	* @param fxValue the fxvalue to fill.
	*/
	void GetVertexData(int vertexIndex, SEMANTIC semantic, FXValue* fxValue);
	/**
	* Specialized function to ge a genaral FXValue object from the vertexlist.
	* @param vertexindex the index of the vertex.
	* @param offset the offset of the element in a vertex.
	* @parm fxValue the fxvalue to fill.
	*/
	void GetVertexData(int vertexIndex, int offset, FXValue* fxValue);
	/**
	* Specialized function to get a general FXValue object from the vertexlist.
	* @param vertexindex the index of the vertex.
	* @param semantic the semantic to get.
	* @param fxValue the fxvalue to fill.
	*/
	void AddVertexData(int vertexIndex, SEMANTIC semantic, FXValue* fxValue);
	/**
	* Specialized function to ge a genaral FXValue object from the vertexlist.
	* @param vertexindex the index of the vertex.
	* @param offset the offset of the element in a vertex.
	* @parm fxValue the fxvalue to fill.
	*/
	void AddVertexData(int vertexIndex, int offset, FXValue* fxValue);

	/**
	* Specialized function to add a DAEFloat3 to the vertexlist.
	* @param vertexIndex the index of the vertex.
	* @param semantic the semantic to set.
	* @param pVector a pointer to the vector.
	*/
	void AddVertexData(int vertexIndex, SEMANTIC semantic, DAEFloat4 * pVector);
	/**
	* Specialized function to add a DAEFloat3 to the vertexlist with the provided
	* semantic offset. The semantic offset is not checked.
	* @param vertexIndex the index of the vertex.
	* @param semanticOffset the position of the semantic in one record.
	* @param pVector a pointer to the vector.
	*/
	void AddVertexData(int vertexIndex, int semanticOffset, DAEFloat4 * pVector);
	/**
	* Specialized function to get a DAEFloat4 from the vertexlist.
	* @param vertexindex the index of the vertex.
	* @param semantic the semantic to get.
	* @param pVector a pointer to the vector.
	*/
	void GetVertexData(int vertexIndex, SEMANTIC semantic, DAEFloat4 *pVector);
	/**
	* Specialized function to get a DAEFloat4 from the vertexlist.
	* @param vertexindex the index of the vertex.
	* @param semanticOffset the offset for the semantic in one record.
	* @param pVector a pointer to the vector.
	*/
	void GetVertexData(int vertexIndex, int semanticOffset, DAEFloat4 *pVector);

	/**
	* Specialized function to add a DAEFloat3 to the vertexlist.
	* @param vertexIndex the index of the vertex.
	* @param semantic the semantic to set.
	* @param pVector a pointer to the vector.
	*/
	void AddVertexData(int vertexIndex, SEMANTIC semantic, DAEFloat3 * pVector);
	/**
	* Specialized function to add a DAEFloat3 to the vertexlist with the provided
	* semantic offset. The semantic offset is not checked.
	* @param vertexIndex the index of the vertex.
	* @param semanticOffset the position of the semantic in one record.
	* @param pVector a pointer to the vector.
	*/
	void AddVertexData(int vertexIndex, int semanticOffset, DAEFloat3 * pVector);
	/**
	* Specialized function to get a DAEFloat3 from the vertexlist.
	* @param vertexindex the index of the vertex.
	* @param semantic the semantic to get.
	* @param pVector a pointer to the vector.
	*/
	void GetVertexData(int vertexIndex, SEMANTIC semantic, DAEFloat3 *pVector);
	/**
	* Specialized function to get a DAEFloat3 from the vertexlist.
	* @param vertexindex the index of the vertex.
	* @param semanticOffset the offset for the semantic in one record.
	* @param pVector a pointer to the vector.
	*/
	void GetVertexData(int vertexIndex, int semanticOffset, DAEFloat3 *pVector);
	/**
	* Specialized function to add a DAEFloat2 to the vertexlist.
	* @param vertexIndex the index of the vertex.
	* @param semantic the semantic to set.
	* @param pVector a pointer to the vector.
	*/
	void AddVertexData(int vertexIndex, SEMANTIC semantic, DAEFloat2 * pVector);
	/**
	* Specialized function to add a DAEFloat2 to the vertexlist.
	* @param vertexIndex the index of the vertex.
	* @param semanticOffset the offset for the semantic in one record.
	* @param pVector a pointer to the vector.
	*/
	void AddVertexData(int vertexIndex, int semanticOffset, DAEFloat2 * pVector);
	/**
	* Specialized function to get a DAEFloat2 from the vertexlist.
	* @param vertexindex the index of the vertex.
	* @param semantic the semantic to get.
	* @param pVector a pointer to the vector.
	*/
	void GetVertexData(int vertexIndex, SEMANTIC semantic, DAEFloat2 *pVector);
	/**
	* Specialized function to get a DAEFloat2 from the vertexlist.
	* @param vertexindex the index of the vertex.
	* @param semanticOffset the ofset for the semantic in one record.
	* @param pVector a pointer to the vector.
	*/
	void GetVertexData(int vertexIndex, int semanticOffset, DAEFloat2 *pVector);
	/**
	* Specialized function to add a single float to the vertexlist.
	* @param vertexIndex the index of the vertex.
	* @param semantic the semantic to set.
	* @param value the value to add to the buffer.
	*/
	void AddVertexData(int vertexIndex, SEMANTIC semantic, float value);
	/**
	* Specialized function to add a single float to the vertexlist.
	* @param vertexIndex the index of the vertex.
	* @param semantic the semantic to set.
	* @param value the value to add to the buffer.
	*/
	void AddVertexData(int vertexIndex, int semanticOffset, float value);
	/**
	* Specialized function to get a single float from the vertexlist.
	* @param vertexindex the index of the vertex.
	* @param semantic the semantic to get.
	* @return the float value.
	*/
	float GetVertexData(int vertexIndex, SEMANTIC semantic);
	/**
	* Specialized function to get a single float from the vertexlist.
	* @param vertexindex the index of the vertex.
	* @param semanticOffset the ofset for the semantic in one record.
	* @return the float value.
	*/
	float GetVertexData(int vertexIndex, int semanticOffset);
	/**
	* Adds a part of a vertex to the vertexlist.
	* @param vertexIndex the index of the vertex.
	* @param semantic the semantic to set.
	* @param dataPtr a ptr to the data that needs to be copied
	* @param size the size of the data to copy.
	*/
	void AddVertexData(int vertexIndex, SEMANTIC s,float * dataPtr, int size);
	/**
	* Gets a part of the vertex from the vertexlist.
	* @param vertexIndex the index of the vertex.
	* @param semantic the semantic to get.
	* @param dataPtr a ptr to the data to set.
	*/
	void GetVertexData(int vertexIndex, SEMANTIC s, float * dataPtr, int size);
	/**
	* Fills a semantic with the provided template.
	* @param semantic the semantic to fill.
	* @param fillValue the value to use for filling the semantic.
	*/
	void FillSemantic(SEMANTIC s, DAEFloat3* fillValue);
	/**
	* Fills a semantic with the provided template.
	* @param semantic the semantic to fill.
	* @param fillValue the value to use for filling the semantic.
	*/ 
	void FillSemantic(SEMANTIC s, FXValue* fillValue);
	/**
	* Normalizes all elements for a given semantic.
	* @param semantic the semantic to normalize.
	*/
	void NormalizeDataBySemantic(SEMANTIC s);

	/**
	* Transform the vector found at a given semantic with the
	* provided matrix. The matrix transformation includes the translation
	* part of the matrix 
	* @param matrix the matrix to use.
	*/
	void TransformVertexSemantic(DAEMatrix& matrix,SEMANTIC s);
	/**
	* Transforms the normal found at a give semantic with
	* provided matrix. The matrix transformation excludes the translation
	* part of the matrix.
	* @param matrix the matrix to use.
	*/
	void TransformNormalSemantic(DAEMatrix& matrix,SEMANTIC s);

	/**
	* Finds the offset for a given semantic.
	* @param semantic the semantic to find the offset for.
	*/
	int GetOffset(SEMANTIC s);
	/**
	* Calculates the normal for a give triangle and adds it to
	* the normal semantic of each vertex.
	* @param v1 the first vertex of the triangle.
	* @param v2 the second vertex for the triangle.
	* @param v3 the third vertex for the triangle.
	*/
	void AddNormalForTri(int v1, int v2, int v3);

	/**
	* Calculates the normal for a give triangle and adds it to
	* the normal semantic of each vertex.
	* @param int posOffset the offset of the position semantic.
	* @param int normalOffset the offset of the normal semantic.
	* @param v1 the first vertex of the triangle.
	* @param v2 the second vertex for the triangle.
	* @param v3 the third vertex for the triangle.
	*/
	void AddNormalForTri(int posOffset, int normalOffset,int v1, int v2, int v3);

	/**
	* Adds the interpolated value of two vertices to the end of this vertexlist. Elements
	* of this vertexlist can tweak this behaviour by setting the interpolation value in 
	* the semantic descriptor objects.
	* @param index1 the index of the first vertex to interpolate.
	* @param index2 the index of the second vertex to interpolate.
	* @param t the interpoloation value.
	* @return the index of the new vertex.
	*/
	int AddInterpolatedVertex(int index1, int index2, float t );
	/**
	* Add the interpolated value of three vertices to the end of this vertexlist using barycentric
	* coordinates. Elements of this vertexlist can tweek this behaviour by setting the interpolation
	* value in the semantic descriptor objects.
	* @param index1 the index of the first vertex to interpolate.
	* @param index2 the index of the second vertex to interpolate.
	* @param index3 the index of the third vertex to interpolate.
	* @param l1 the first barycentric coordinate.
	* @param l2 the second barycentric coordinate.
	* @param l3 the third barycentric coordinate.
	*/ 
	int AddInterpolatedVertex(int index1, int index2, int index3, float l1, float l2, float l3);
	/**
	* Recalculates the data of a vertex as a combination of three vertices.
	* @param vertexIndex the index of the vertex to change.
	* @param index1 the index of the first vertex to interpolate.
	* @param index2 the index of the second vertex to interpolate.
	* @param index3 the index of the third vertex to interpolate.
	* @param l1 the first barycentric coordinate.
	* @param l2 the second barycentric coordinate.
	* @param l3 the third barycentric coordinate.
	*/
	void SetInterpolatedVertex(int vertexIndex,int index1, int index2, int index3, float l1, float l2, float l3);

	/**
	* Returns the size per vertex.
	* @return the size per vertex.
	*/
	inline int GetSizePerVertexInBytes(){return m_SizePerVertex;} 
	/**
	* Returns the number of semantics.
	* @return the number of semantics.
	*/
	size_t GetNumberOfSemantics(){return m_SemanticDescriptorList.size();}
	/**
	* Returns the semantic
	* @param index the index of the semantic.
	* @return the SEMANTIC value.
	*/
	SEMANTIC GetSemantic(unsigned int index);
	/**
	* Returns the size of the semantic.
	* @param index the index of the semantic.
	* @return the size of the semantic (not in bytes)
	*/
	int GetSemanticSize(unsigned int index);
	/**
	* Returns the datatype of the given index.
	* @param index the index of the datatype.
	* @return the datatype of the sematic.
	*/
	DATATYPE GetSemanticDataType(unsigned int index);

	/**
	* Returns a pointer to the float data.
	* @return the raw pointer to the underlying data.
	*/
	inline byte* GetData(){return m_Data;}
	/**
	* Write this VertexList object to a binary stream.
	* This function will write:
	*
	* 1) A constant that indicates that this is a vertexlist. (4 bytes)
	* 2) The total size in bytes (4 bytes)
	* 3) The number of vertices in this vertexlist. (4 bytes)
	* 4) The number of supported semantics (4 bytes)
	* 5) For each semantic , the type of semantic (as defined in the enum) (4 bytes)
	* followed by the size for the semantic.
	* 6) the blob with all the vertex data.
	*/
	void Write(fstream stream);
	// Interpolation support

	void AddInterpolatedFloatVertex(int index, int offset, int nrOfElements,bool interpolate,int index1,int index2,float t);
	void AddInterpolatedSignedIntVertex(int index, int offset, int nrOfElements,bool interpolate,int index1,int index2,float t);
	void AddInterpolatedSignedShortVertex(int index, int offset, int nrOfElements,bool interpolate,int index1,int index2,float t);
	void AddInterpolatedSignedByteVertex(int index, int offset, int nrOfElements,bool interpolate,int index1,int index2,float t);
	void AddInterpolatedUnsignedIntVertex(int index, int offset, int nrOfElements,bool interpolate,int index1,int index2,float t);
	void AddInterpolatedUnsignedShortVertex(int index, int offset, int nrOfElements,bool interpolate,int index1,int index2,float t);
	void AddInterpolatedUnsignedByteVertex(int index, int offset, int nrOfElements,bool interpolate,int index1,int index2,float t);

	void AddInterpolatedFloatVertex(int index, int offset, int nrOfElements,bool interpolate,int index1,int index2,int index3, float l1, float l2, float l3);
	void AddInterpolatedSignedIntVertex(int index, int offset, int nrOfElements,bool interpolate,int index1,int index2,int index3, float l1, float l2, float l3);
	void AddInterpolatedSignedShortVertex(int index, int offset, int nrOfElements,bool interpolate,int index1,int index2,int index3, float l1, float l2, float l3);
	void AddInterpolatedSignedByteVertex(int index, int offset, int nrOfElements,bool interpolate,int index1,int index2,int index3, float l1, float l2, float l3);
	void AddInterpolatedUnsignedIntVertex(int index, int offset, int nrOfElements,bool interpolate,int index1,int index2,int index3, float l1, float l2, float l3);
	void AddInterpolatedUnsignedShortVertex(int index, int offset, int nrOfElements,bool interpolate,int index1,int index2,int index3, float l1, float l2, float l3);
	void AddInterpolatedUnsignedByteVertex(int index, int offset, int nrOfElements,bool interpolate,int index1,int index2,int index3, float l1, float l2, float l3);

	// Static functions
	/**
	* Creates a standard vertex buffer with 3D positions.
	* @param initialSize the initialSize for the vertex list.
	* @return a vertex list with one POSITION semantic.
	*/
	static VertexList* Create3DPosVL(int initialSize);
	/**
	* Creates a standard vertex buffer with 3D positions and colors.
	* @param initialSize the initialSize for the vertex list.
	* @return a vertex list with one POSITION semantic.
	*/
	static VertexList* Create3DPosColorVL(int initialSize);
	/**
	* Creates a standard vertex buffer with 3D positions and uv coordinates.
	* @param initialSize the initialSize for the vertex list.
	* @return a vertex list with one POSITION and TEXCOORD semantic.
	*/
	static VertexList* Create3DPosTexVL(int initialSize);
	/**
	* Creates a standard vertex buffer with 3D positions,uv coordinates and 3D normals.
	* @param initialSize the initialSize for the vertex list.
	* @return a vertex list with one POSITION,TEXCOORD and NORMAL semantic.
	*/
	static VertexList* Create3DPosTexNormalVL(int initialSize);
private:
	/**
	* the number of floats per vertex.
	*/
	int m_SizePerVertex;
	/**
	* the data.
	*/
	byte * m_Data;
	/**
	* the initial size for the vertex list (in number of vertices)
	*/
	int m_InitialSize;
	/**
	* the current capacity for the vertexlist.
	*/
	int m_CurrentCapacity;
	/**
	* the current size minus 1.
	*/
	int m_CurrentSize;
	/**
	* The semantics that are used by this vertexlist.
	*/
	vector<SemanticDescriptor> m_SemanticDescriptorList;

	/**
	* Gets the size in bytes of dataType.
	* @param dataType the dataType to get the size from.
	* @return the size in bytes.
	*/
	int GetSizeInBytes(DATATYPE dataType);

	/**
	* Grows the vertex list to the provided capacity (in number of vertices.
	* @param numberOfVertices the new number of vertices.
	*/
	void Grow(int numberOfVertices);
	/**
	* Finds the offset for a given semantic and vertexindex
	* @param vertexIndex the index for the vertex.
	* @param semantic the semantic to find the offset for.
	*/
	int GetOffset(int vertexIndex,SEMANTIC s);
	
	/**
	* Gets the size of a give semantic.
	* @param s the semantic to get the size for.
	*/
	int GetSemanticSize(SEMANTIC s);
	/**
	* Update the current maximal index.
	* @param latestIndex the latest index that was added.
	*/
	void UpdateCurrentSize(int index);
};